/*
 * Copyright 2007-2009 Thomas Krause (thomas@berlin-reloaded.de)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gidoo.cms.jcr;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import org.gidoo.cms.helper.Tools;
import org.gidoo.cms.login.GidooCMSSession;

/**
 * Static helper classes to manipulate the jcr repository.
 * @author <a href="mailto:thomas@berlin-reloaded.de">Thomas Krause</a>
 */
public class JcrHelper
{
  
  /** The name of the special meta data node of each cms page. */
  public static String META_DATA_NODE_NAME = "cms:md";
  
  private static Pattern patternMetaDataCheck = 
    Pattern.compile("(^|/)" + META_DATA_NODE_NAME + "($|/)");
  
  /**
   * Returns the section to which a path of the CMS belongs to.
   * @param path The path of the CMS.
   * @return The section or null if no section was found.
   */
  public static Node getSectionByPath(Node rootNode, String path) 
    throws RepositoryException
  {
    if(path == null || rootNode == null)
      return null;
    
    NodeIterator sectionsIt = rootNode.getNode("sections").getNodes();
    
    // some cleanup
    path = Tools.cleanupFolderPath(path);
    
    int bestMatchLength = -1;
    Node bestMatchSection = null;
    
    while(sectionsIt.hasNext())
    {
      Node s = sectionsIt.nextNode();
      if(s.hasProperty("cms:url"))
      {
        String u = s.getProperty("cms:url").getString();
      
        Pattern p = Pattern.compile(u);
        Matcher m = p.matcher(path);

        // does the string begin with the pattern and is it longer than the last match?
        if(m.find() && m.start() == 0 && u.length() > bestMatchLength)
        {
          // true if either the string matches exactly or if the next character is a "/"
          if(u.length() == 0 || path.length() == m.end() || path.charAt(m.end()) == '/')
          {
            bestMatchLength = u.length();
            bestMatchSection = s;
          }
        }
      }
    }
    
    return bestMatchSection;
  }
  
  /**
   * Retreive a page by it's path.
   * 
   * @param path The path (without the section-part).
   * @param section The section.
   * @return The folder belonging to this path or null if not found or not published.
   */
  public static Node getPublishedPageByPath(String path, Node section, 
    GidooCMSSession session) throws RepositoryException
  {
    if(path == null || section == null)
      return null;
    
    if("".equals(path) || "/".equals(path))
    {
      // empty relative path (without section) -> must be the section
      if(section.hasProperty("cms:xml") && 
        !"".equals(section.getProperty("cms:xml").getString()))
      {
        return section;
      }
      else
      {
        return null;
      }
    }
    
    path = Tools.cleanupFolderPath(path);
    
    // check for meta data node in path
    if(patternMetaDataCheck.matcher(path).find())
    {
      return null;
    }
    
    try
    {
      Node n = section.getNode(path);
      String t = n.getPrimaryNodeType().getName();
      if(isPublished(n))
      {
        return n;
      }
    }
    catch(PathNotFoundException ex)
    {
    }
    
    return null;
  }
  
  /**
   * Retreive the page by it's path.
   * 
   * @param path The path of the page (with section part).  
   * @return The folder or null if not found or not published.
   */
  public static Node getPublishedPageByPath(String path, GidooCMSSession session) 
    throws RepositoryException
  {
    if(path == null || session == null)
      return null;
    
    Node section = getSectionByPath(session.getJcrRootNode(), path);
    if(section == null)
      return null;
    
    String urlWithoutSection = 
      path.substring(section.getProperty("cms:url").getString().length());

    return getPublishedPageByPath(urlWithoutSection, section, session);
  }
  
  
  /**
   * Retreive a published resource by the path.
   * 
   * @param path The path of the Resource (with section part)
   * @return The resource or null if not found or not published.
   * @throws org.gidoo.cms.dbconnection.DatabaseConnectionException
   */
  public static Node getResourceByPath(String path, GidooCMSSession session)
    throws RepositoryException
  {
    if(path == null)
      return null;
    
    Node section = getSectionByPath(session.getJcrRootNode(), path);
    if(section == null)
      return null;
    
    String urlWithoutSection = 
      path.substring(section.getProperty("cms:url").getString().length());
    
    urlWithoutSection = Tools.cleanupFolderPath(urlWithoutSection);
    
    if("".equals(urlWithoutSection))
    {
      return null;
    }
    
    try
    {
      Node n = section.getNode(urlWithoutSection);
      if("cms:resource".equals(n.getPrimaryNodeType().getName()))
      {
        return n;
      }
      
    }
    catch(PathNotFoundException ex)
    {
      
    }
    
    return null;
  }
  
  
  /** Returns the section node or null if not a valid folder. */
  public static Node getSectionOfNode(Node folder)
  {
    try
    {
      Node jcrRootNode = folder.getSession().getRootNode();
      
      Node sectionsNode = jcrRootNode.getNode("sections");
      Node curFolder = folder;
      while(!curFolder.getParent().getPath().equals(sectionsNode.getPath()))
      {
        curFolder = curFolder.getParent();
      }
      
      return curFolder;
    }
    catch (RepositoryException ex)
    {
    }
    
    return null;
  }
  
    
  /**
   * Copy important properties from CMS-pages.
   * 
   * @param from The node to copy from.
   * @param to   The Node to copy the properties into. 
   */
  public static void copyPageProperties(Node from, Node to) throws RepositoryException
  {
    String[] prop2save = new String[]
    {
      "cms:xml", "cms:title", "cms:editor", "cms:created", "cms:lastModified"
    };

    for(String s : prop2save)
    {
      if(from.hasProperty(s))
      {
        if(from.getProperty(s).getType() == PropertyType.STRING)
        {
          to.setProperty(s, from.getProperty(s).getString());
        }
        else if(from.getProperty(s).getType() == PropertyType.DATE)
        {
          to.setProperty(s, from.getProperty(s).getDate());
        }
      }
    }
  }
  
  /**
   * Get the URL (without protocol, host, and context) of the node.
   * 
   * @param node The node.
   * @return The url or null if node was not valid gidooCMS-node. 
   */
  public static String getPartialURLForNode(Node node) throws RepositoryException
  {
    String url = null;

    if(node == null)
    {
      return null;
    }

    String type = node.getPrimaryNodeType().getName();
    if("cms:section".equals(type))
    {
      url = node.getProperty("cms:url").getString();
    }
    else if("cms:page".equals(type))
    {
      Node s = JcrHelper.getSectionOfNode(node);
      if(s != null)
      {
        String urlProperty = s.getProperty("cms:url").getString();
        url = ("".equals(urlProperty) ? "" : urlProperty + "/" ) +
        node.getPath().substring(s.getPath().length()+1);
      }
    }
    
    return url;
  }
  
  /**
   * Check wether a page is a valid gidooCMS page and published.
   * @param page The page to check, should be of type "cms:page" or "cms:section".
   * @return True if this is a valid published page, false otherwise.
   */
  public static boolean isPublished(Node page)
  {
    if(page != null)
    {
      try
      {
        String type = page.getPrimaryNodeType().getName();
        if("cms:page".equals(type) || "cms:section".equals(type))
        {
          if(!"".equals(page.getProperty("cms:xml").getString()))
          {
            return true;
          }
        }
      }
      catch(RepositoryException ex)
      {
        return false;
      }
    }
    return false;
  }
  
}
